function conf = spp_config(varargin)
% Set up configuration variables.
%   conf = spp_config(varargin)
%
% Adapted from spp code written by Ross Girshick
% AUTORIGHTS
% ---------------------------------------------------------
% Copyright (c) 2014, Shaoqing Ren
% 
% This file is part of the SPP code and is available 
% under the terms of the Simplified BSD License provided in 
% LICENSE. Please retain this notice and LICENSE if you use 
% this file (or any portion of it) in your project.
% ---------------------------------------------------------
% Copyright (c) 2014, Ross Girshick
% 
% This file is part of the R-CNN code and is available 
% under the terms of the Simplified BSD License provided in 
% LICENSE. Please retain this notice and LICENSE if you use 
% this file (or any portion of it) in your project.
% ---------------------------------------------------------

% ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
% Defaults config (override in spp_config_local.m)
% ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
% If you want to override any of these, create a **script** 
% named spp_config_local.m and redefine these variables there.

% Experiments directory. The directory under which most outputs
% generated by running this code will go.
EXP_DIR = fullfile(pwd, 'cachedir');

% Set to false if you do not want to use a GPU.
try
USE_GPU = logical(gpuDeviceCount());
catch
    % gpuDeviceCount not available. assume GPU mode
    USE_GPU=true;
end

% Load local overrides if rccn_config_local.m exists
% See spp_config_local.example.m for an example
if exist('spp_config_local.m')
  spp_config_local;
end


% ~~~~~~~~~~~~~~~~~~~~~~ ADVANCED SETUP BELOW ~~~~~~~~~~~~~~~~~~~~~~
% 
% conf            top-level variables
%
% To set a configuration override file, declare
% the global variable spp_CONFIG_OVERRIDE 
% and then set it as a function handle to the
% config override function. E.g.,
%  >> global spp_CONFIG_OVERRIDE;
%  >> spp_CONFIG_OVERRIDE = @my_spp_config;
% In this example, we assume that you have an M-file 
% named my_spp_config.m. 
%
% Overrides passed in as arguments have the highest precedence.
% Overrides in the overrides file have second highest precedence,
% but are clobbered by overrides passed in as arguments.
% Settings in this file are clobbered by the previous two.

% Configuration structure
conf = [];

% ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
% 
% Persistent and per-call overrides
%
% Check for an override configuration file
% assert_not_in_parallel_worker();
% global spp_CONFIG_OVERRIDE;
% if ~isempty(spp_CONFIG_OVERRIDE)
%   conf = spp_CONFIG_OVERRIDE();
% end

% Clobber with overrides passed in as arguments
for i = 1:2:length(varargin)
  key = varargin{i};
  val = varargin{i+1};
  eval(['conf.' key ' = val;']);
end
% ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

conf = cv(conf, 'use_gpu', USE_GPU);
conf = cv(conf, 'exp_dir', EXP_DIR);
conf = cv(conf, 'sub_dir', '');
conf = cv(conf, 'cache_dir', fullfile(conf.exp_dir, conf.sub_dir, '/'));

mkdir_if_missing(conf.cache_dir);


% -------------------------------------------------------------------
% Helper functions
% -------------------------------------------------------------------

% -------------------------------------------------------------------
% Does nothing if conf.key exists, otherwise sets conf.key to val
function conf = cv(conf, key, val)
try
  eval(['conf.' key ';']);
catch
  eval(['conf.' key ' = val;']);
end


% -------------------------------------------------------------------
% Throw an error if this function is called from inside a matlabpool
% worker.
function assert_not_in_parallel_worker()
% Matlab does not support accessing global variables from
% parallel workers. The result of reading a global is undefined
% and in practice has odd and inconsistent behavoir. 
% The configuraton override mechanism relies on a global
% variable. To avoid hard-to-find bugs, we make sure that
% spp_config cannot be called from a parallel worker.

t = [];
if usejava('jvm')
  try
    t = getCurrentTask();
  catch 
  end
end

if ~isempty(t)
  msg = ['spp_config() cannot be called from a parallel worker ' ...
         '(or startup.m did not run -- did you run matlab from the ' ...
         'root of the spp installation directory?'];
  error(msg);
end
